package com.demo.security.access.method;

import java.lang.reflect.Method;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.springframework.util.ObjectUtils;

import com.demo.security.access.LogicAttribute;

/**
 * 方法元数据代理资源
 * 
 * @author ryuu.kk
 */
public class DelegatingMethodLogicMetadataSource extends AbstractMethodLogicMetadataSource {

	private final static List<LogicAttribute> NULL_CONFIG_ATTRIBUTE = Collections.emptyList();

	private final List<MethodLogicMetadataSource> methodLogicMetadataSources;
	private final Map<DefaultCacheKey, Collection<LogicAttribute>> attributeCache = new HashMap<DefaultCacheKey, Collection<LogicAttribute>>();
	/**
	 * 构造函数
	 * @param methodLogicMetadataSources 方法的逻辑调用元数据
	 */
	public DelegatingMethodLogicMetadataSource(List<MethodLogicMetadataSource> methodLogicMetadataSources) {
		this.methodLogicMetadataSources = methodLogicMetadataSources;
	}
	@Override
    public Collection<LogicAttribute> getAttributes(Method method, Class<?> targetClass) {
        DefaultCacheKey cacheKey = new DefaultCacheKey(method, targetClass);
        synchronized (attributeCache) {
            Collection<LogicAttribute> cached = attributeCache.get(cacheKey);
            // Check for canonical value indicating there is no config attribute,
            if (cached != null) {
                return cached;
            }
            // No cached value, so query the sources to find a result
            Collection<LogicAttribute> attributes = null;
            for (MethodLogicMetadataSource s : methodLogicMetadataSources) {
                attributes = s.getAttributes(method, targetClass);
                if (attributes != null && !attributes.isEmpty()) {
                    break;
                }
            }
            // Put it in the cache.
            if (attributes == null || attributes.isEmpty()) {
                this.attributeCache.put(cacheKey, NULL_CONFIG_ATTRIBUTE);
                return NULL_CONFIG_ATTRIBUTE;
            }
            if (logger.isDebugEnabled()) {
                logger.debug("Caching method [" + cacheKey + "] with attributes " + attributes);
            }
            this.attributeCache.put(cacheKey, attributes);

            return attributes;
        }
    }
    
	@Override
	public Collection<LogicAttribute> getAllAttributes() {
		  Set<LogicAttribute> set = new HashSet<LogicAttribute>();
	        for (MethodLogicMetadataSource s : methodLogicMetadataSources) {
	            Collection<LogicAttribute> attrs = s.getAllAttributes();
	            if (attrs != null) {
	                set.addAll(attrs);
	            }
	        }
	        return set;
	}

	// ~ Inner Classes
	// ==================================================================================================

	private static class DefaultCacheKey {
		private final Method method;
		private final Class<?> targetClass;

		public DefaultCacheKey(Method method, Class<?> targetClass) {
			this.method = method;
			this.targetClass = targetClass;
		}

		public boolean equals(Object other) {
			DefaultCacheKey otherKey = (DefaultCacheKey) other;
			return (this.method.equals(otherKey.method) && ObjectUtils
					.nullSafeEquals(this.targetClass, otherKey.targetClass));
		}

		public int hashCode() {
			return this.method.hashCode()
					* 21
					+ (this.targetClass != null ? this.targetClass.hashCode()
							: 0);
		}

		public String toString() {
			return "CacheKey["
					+ (targetClass == null ? "-" : targetClass.getName())
					+ "; " + method + "]";
		}
	}
}
